//The MIT License

//Copyright (c) 2009 Lluis Sanchez, Nick Darnell

//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:

//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.

//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.

using System;
using System.Reflection;

namespace Polymorph
{
    public static class Exp
    {
        public static CodeExpression Literal(object ob) { return new CodeLiteral(ob); }
        public static CodeExpression Literal(Type type, object ob) { return new CodeLiteral(type, ob); }
        public static CodeExpression Literal<T>(object ob) { return new CodeLiteral(typeof(T), ob); }

        public static CodeExpression Literal(string ob) { return new CodeLiteral(typeof(string), ob); }

        public static CodeExpression New(Type type, params CodeExpression[] pars)
        {
            return new CodeNew(type, pars);
        }

        public static CodeExpression New<T>(params CodeExpression[] pars)
        {
            return new CodeNew(typeof(T), pars);
        }

        public static CodeExpression NewArray(Type type, CodeExpression size)
        {
            return new CodeNewArray(type, size);
        }

        public static CodeExpression NewArray<T>(CodeExpression size)
        {
            return new CodeNewArray(typeof(T), size);
        }

        public static CodeExpression NewArray(Type type, int n)
        {
            return new CodeNewArray(type, Exp.Literal(n));
        }

        public static CodeExpression NewArray<T>(int n)
        {
            return new CodeNewArray(typeof(T), Exp.Literal(n));
        }

        public static CodeExpression And(CodeExpression e1, CodeExpression e2)
        {
            return new CodeAnd(e1, e2);
        }

        public static CodeExpression And(CodeExpression e1, CodeExpression e2, CodeExpression e3)
        {
            return new CodeAnd(new CodeAnd(e1, e2), e3);
        }

        public static CodeExpression Or(CodeExpression e1, CodeExpression e2)
        {
            return new CodeOr(e1, e2);
        }

        public static CodeValueReference Increment(CodeValueReference e)
        {
            return new CodeIncrement(e);
        }

        public static CodeValueReference Decrement(CodeValueReference e)
        {
            return new CodeDecrement(e);
        }

        public static CodeExpression NullCoalesce(CodeExpression e1, CodeExpression e2)
        {
            return new CodeNullCoalesce(e1, e2);
        }

        public static CodeExpression Call(CodeExpression target, string name, params CodeExpression[] parameters)
        {
            return new CodeMethodCall(target, name, parameters);
        }

        public static CodeExpression Call(CodeExpression target, MethodInfo method, params CodeExpression[] parameters)
        {
            return new CodeMethodCall(target, method, parameters);
        }

        public static CodeExpression Call(CodeExpression target, CodeMethod method, params CodeExpression[] parameters)
        {
            return new CodeMethodCall(target, method, parameters);
        }

        public static CodeExpression Call(Type type, string name, params CodeExpression[] parameters)
        {
            return new CodeMethodCall(type, name, parameters);
        }

        public static CodeExpression Call<T>(string name, params CodeExpression[] parameters)
        {
            return new CodeMethodCall(typeof(T), name, parameters);
        }

        public static CodeExpression Call(MethodInfo method, params CodeExpression[] parameters)
        {
            return new CodeMethodCall(method, parameters);
        }

        public static CodeExpression Call(CodeMethod method, params CodeExpression[] parameters)
        {
            return new CodeMethodCall(method, parameters);
        }

        public static CodeExpression NullValue(Type type)
        {
            return new CodeLiteral(type, null);
        }

        public static CodeExpression NullValue<T>()
        {
            return new CodeLiteral(typeof(T), null);
        }

        public static CodeExpression When(CodeExpression condition, CodeExpression trueResult, CodeExpression falseResult)
        {
            return new CodeWhen(condition, trueResult, falseResult);
        }

        public static CodeFieldReference GetField<T>(String name)
        {
            return GetField(typeof(T), name);
        }

        public static CodeFieldReference GetField(Type type, String name)
        {
            FieldInfo field = type.GetField(name,
                BindingFlags.Public | BindingFlags.Instance |
                BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

            return new CodeFieldReference(null, field);
        }

        public static CodePropertyReference GetProperty<T>(String name)
        {
            return GetProperty(typeof(T), name);
        }

        public static CodePropertyReference GetProperty(Type type, String name)
        {
            PropertyInfo property = type.GetProperty(name,
                BindingFlags.Public | BindingFlags.Instance |
                BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

            return new CodePropertyReference(null, property);
        }

        public static CodeValueReference GetMember<T>(String name)
        {
            return GetMember(typeof(T), name);
        }

        public static CodeValueReference GetMember(Type type, string name)
        {
            MemberInfo[] mems = type.GetMember(name,
                BindingFlags.Public | BindingFlags.Instance |
                BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

            if (mems.Length == 0)
                throw new InvalidOperationException("Field or Property'" + name + "' not found in " + type);

            return GetMember(mems[0]);
        }

        public static CodeValueReference GetMember(MemberInfo member)
        {
            if (member is FieldInfo)
                return new CodeFieldReference(null, (FieldInfo)member);
            else if (member is PropertyInfo)
                return new CodePropertyReference(null, (PropertyInfo)member);
            else
                throw new InvalidOperationException(member.Name + " is not either a field or a property");
        }

        public static bool CanGenerateLiteral<T>()
        {
            return CanGenerateLiteral(typeof(T));
        }

        public static bool CanGenerateLiteral(Type type)
        {
            return CodeLiteral.CanGenerate(type);
        }
    }
}